package com.ssy.lingxi.enhance.serviceimpl.process;

import cn.hutool.core.collection.CollectionUtil;
import com.querydsl.core.BooleanBuilder;
import com.ssy.lingxi.common.exception.BusinessException;
import com.ssy.lingxi.common.model.dto.UserLoginCacheDTO;
import com.ssy.lingxi.common.response.PageData;
import com.ssy.lingxi.common.response.ResponseCode;
import com.ssy.lingxi.common.response.Wrapper;
import com.ssy.lingxi.enhance.api.model.dto.BatchExamDTO;
import com.ssy.lingxi.enhance.api.model.dto.UpdateInvoiceDTO;
import com.ssy.lingxi.enhance.constant.*;
import com.ssy.lingxi.enhance.entity.*;
import com.ssy.lingxi.enhance.handler.async.MessageAsyncService;
import com.ssy.lingxi.enhance.handler.async.ReportAsyncService;
import com.ssy.lingxi.enhance.handler.jpa.QueryDslUtil;
import com.ssy.lingxi.enhance.model.bo.PNOReceiveDeliverDetailProductBO;
import com.ssy.lingxi.enhance.model.dto.AddInvoiceDTO;
import com.ssy.lingxi.enhance.model.dto.CommonIdDTO;
import com.ssy.lingxi.enhance.model.qo.ProduceNoticeOrderQO;
import com.ssy.lingxi.enhance.model.vo.ProduceNoticeOrderListVO;
import com.ssy.lingxi.enhance.repository.*;
import com.ssy.lingxi.enhance.service.process.ProcessToBeAddDeliveryService;
import com.ssy.lingxi.message.api.constant.MessageTemplateCode;
import com.ssy.lingxi.product.api.enums.InvoicesTypeEnum;
import com.ssy.lingxi.product.api.feign.InvoicesControllerFeign;
import com.ssy.lingxi.product.api.model.request.warehouse.InvoicesAddFeignRequest;
import com.ssy.lingxi.product.api.model.request.warehouse.InvoicesDetailsListRequest;
import com.ssy.lingxi.product.api.model.request.warehouse.InvoicesDetailsRequest;
import com.ssy.lingxi.product.api.model.response.warehouse.InvoicesDetailsResponse;
import com.ssy.lingxi.report.api.enums.EnhanceProcessOperateTypeEnum;
import com.ssy.lingxi.report.api.enums.OperateDataSourceEnum;
import com.ssy.lingxi.workflow.api.feign.ProcessFeign;
import com.ssy.lingxi.workflow.api.model.contant.ProcessTaskStatusEnum;
import com.ssy.lingxi.workflow.api.model.vo.request.TaskExecuteVO;
import com.ssy.lingxi.workflow.api.model.vo.response.SimpleTaskCompleteVO;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import javax.transaction.Transactional;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * 加工能力 - 确认生产通知单(加工企业) - 待新增加工发货单 - 业务实现层
 * @author fhj
 * @date 2020/10/20
 * @version 2.0.0
 */
@Slf4j
@Service
public class ProcessToBeAddDeliveryServiceImpl implements ProcessToBeAddDeliveryService {

    @Resource
    private ProcessFeign processFeign;
    @Resource
    private QueryDslUtil queryDslUtil;
    @Resource
    private ProduceNoticeOrderRepository produceNoticeOrderRepository;
    @Resource
    private ProduceNoticeOrderDetailRepository produceNoticeOrderDetailRepository;
    @Resource
    private InvoicesControllerFeign invoicesControllerFeign;
    @Resource
    private InnerWorkflowRecordRepository innerWorkflowRecordRepository;
    @Resource
    private OuterWorkflowRecordRepository outerWorkflowRecordRepository;
    @Resource
    private PNOReceiveDeliverDetailRepository pnoReceiveDeliverDetailRepository;
    @Resource
    private ReportAsyncService reportAsyncService;
    @Resource
    private MessageAsyncService messageAsyncService;

    /**
     * 列表
     * */
    @Override
    public Wrapper<PageData<ProduceNoticeOrderListVO>> list(ProduceNoticeOrderQO qo, UserLoginCacheDTO user) {
        //查询表实体 - 生产通知单
        QProduceNoticeOrderDO pno = QProduceNoticeOrderDO.produceNoticeOrderDO;

        //查询条件构造 - 必要条件
        BooleanBuilder predicates = new BooleanBuilder();
        predicates.and(pno.processMemberId.eq(user.getMemberId()));
        predicates.and(pno.processRoleId.eq(user.getMemberRoleId()));
        predicates.and(pno.outerStatus.eq(PNOOuterStatusEnum.TO_BE_ADD_DELIVERY.getCode()));

        return Wrapper.success(queryDslUtil.getProcessPNOList(qo, pno, predicates));
    }

    /**
     * 提交
     * */
    @Override
    @Transactional
    public Wrapper<Void> submit(AddInvoiceDTO dto, UserLoginCacheDTO user) {
        //根据ID获取生产通知单详情
        ProduceNoticeOrderDO produceNoticeOrder = produceNoticeOrderRepository.findById(dto.getProduceNoticeOrderId()).orElse(null);
        if(produceNoticeOrder == null) {
            return Wrapper.fail(ResponseCode.ENHANCE_BY_ID_NOT_FOUND_PNO);
        }
        if(!produceNoticeOrder.getStatus().equals(PNOAllStatusEnum.TO_BE_PROCESS_ADD_DELIVERY.getCode())) {
            return Wrapper.fail(ResponseCode.ENHANCE_ONLY_SUBMIT_INNER_STATUS_AS_TO_BE_PROCESS_ADD_DELIVERY);
        }

        //获取生产通知单的配送方式（是否为物流）
        boolean logistics = produceNoticeOrder.getDeliveryType().equals(DeliveryTypeEnum.LOGISTICS.getCode());

        //调用仓库服务 -> 添加单据内部接口
        InvoicesAddFeignRequest request = new InvoicesAddFeignRequest();
        request.setInvoicesTypeCode(InvoicesTypeEnum.PROCESSING_INVOICE.getCode());
        request.setInvoicesTypeId(InvoicesTypeEnum.PROCESSING_INVOICE.getId());
        request.setInvoicesType(InvoicesTypeEnum.PROCESSING_INVOICE.getName());
        request.setInvoicesAbstract(dto.getInvoiceSummary());
        request.setInventoryId(dto.getWarehouseId());
        request.setInventoryRole(dto.getWarehouseRole());
        request.setTransactionTime(dto.getInvoiceTime());
        request.setOrderNo(produceNoticeOrder.getNoticeNo());
        request.setState(1);
        request.setRelevanceInvoicesNo(produceNoticeOrder.getNoticeNo());
        request.setRelevanceInvoicesId(produceNoticeOrder.getId());
        request.setRelevanceInvoices(4);
        request.setDeliveryAddresId(logistics ? produceNoticeOrder.getReceiverAddressId() : produceNoticeOrder.getDeliveryAddressId());
        request.setReceiverName(logistics ? produceNoticeOrder.getReceiveUserName() : produceNoticeOrder.getDeliveryUserName());
        request.setFullAddress(logistics ? produceNoticeOrder.getReceiveAddress() : produceNoticeOrder.getDeliveryAddress());
        request.setPhone(logistics ? produceNoticeOrder.getReceiveUserTel() : produceNoticeOrder.getDeliveryUserTel());
        request.setDeliveryType(produceNoticeOrder.getDeliveryType());
        request.setCreateRoleId(user.getMemberRoleId());
        request.setCreateMemberId(user.getMemberId());
        request.setCreateMemberName(user.getCompany());
        request.setSupplyMembersName(user.getCompany());
        request.setSupplyMembersId(user.getMemberId());
        request.setSupplyMembersRoleId(user.getMemberRoleId());
        request.setSource(dto.getSource());
        request.setInvoicesDetailsRequests(dto.getInvoiceDetailsDTOList().stream().map(a -> {
            InvoicesDetailsRequest invoicesDetailsRequest = new InvoicesDetailsRequest();
            BeanUtils.copyProperties(a, invoicesDetailsRequest);
//            invoicesDetailsRequest.setGoodsName(a.getItemName());
//            invoicesDetailsRequest.setProductCount(a.getProcessNum());
            return invoicesDetailsRequest;
        }).collect(Collectors.toList()));
        Wrapper<InvoicesDetailsResponse> invoicesDetailsResponseWrapper = invoicesControllerFeign.invoicesAddFeign(request);
        if(ResponseCode.SUCCESS.getCode() == invoicesDetailsResponseWrapper.getCode()) {
            //根据生产通知单明细ID进行分组, 获取到每个生产通知单明细的发货数量总和。【key:生产通知单明细ID, value:发货数量总和】
            Map<Long, Double> map = dto.getInvoiceDetailsDTOList().stream().collect(Collectors.groupingBy(AddInvoiceDTO.InvoiceDetailsDTO::getProduceNoticeOrderDetailId, Collectors.summingDouble(AddInvoiceDTO.InvoiceDetailsDTO::getShipmentQuantity)));

            //获取生产通知单明细集合, 拼装生产通知单收发货明细商品（注意这里keySet：这里获取的生产通知单明细并非是生产通知单下的所有明细, 而是选择发货的货品对应的生产通知单明细）
            List<ProduceNoticeOrderDetailDO> produceNoticeOrderDetailDOList = produceNoticeOrderDetailRepository.findAllById(map.keySet());
            List<PNOReceiveDeliverDetailProductBO> pnoReceiveDeliverDetailProductBOList = produceNoticeOrderDetailDOList.stream().map(a -> {
                PNOReceiveDeliverDetailProductBO pnoReceiveDeliverDetailProductBO = new PNOReceiveDeliverDetailProductBO();
                BeanUtils.copyProperties(a, pnoReceiveDeliverDetailProductBO);
                pnoReceiveDeliverDetailProductBO.setDeliverNum(map.get(a.getId()));
                pnoReceiveDeliverDetailProductBO.setDifferenceNum(pnoReceiveDeliverDetailProductBO.getDeliverNum());
                pnoReceiveDeliverDetailProductBO.setProduceNoticeOrderDetailId(a.getId());
                return pnoReceiveDeliverDetailProductBO;
            }).collect(Collectors.toList());

            //新增生产通知单收发货明细
            PNOReceiveDeliverDetailDO produceNoticeOrderDeliverDetail = new PNOReceiveDeliverDetailDO();
            produceNoticeOrderDeliverDetail.setProduceNoticeOrderId(dto.getProduceNoticeOrderId());
            produceNoticeOrderDeliverDetail.setProcessTotalPrice(produceNoticeOrder.getProcessTotalPrice());
            produceNoticeOrderDeliverDetail.setNoticeNo(produceNoticeOrder.getNoticeNo());
            produceNoticeOrderDeliverDetail.setSummary(produceNoticeOrder.getSummary());
            produceNoticeOrderDeliverDetail.setProduceNoticeOrderTime(produceNoticeOrder.getCreateTime());
            produceNoticeOrderDeliverDetail.setDeliveryId(invoicesDetailsResponseWrapper.getData().getId());
            produceNoticeOrderDeliverDetail.setDeliveryNo(invoicesDetailsResponseWrapper.getData().getInvoicesNo());
            produceNoticeOrderDeliverDetail.setDeliveryBatch(produceNoticeOrder.getDeliveryBatch() + 1);
            produceNoticeOrderDeliverDetail.setPnoReceiveDeliverDetailProductBOList(pnoReceiveDeliverDetailProductBOList);
            pnoReceiveDeliverDetailRepository.saveAndFlush(produceNoticeOrderDeliverDetail);

            //更新发货单ID、发货单号、发货批次、状态、加工企业内部状态
            produceNoticeOrder.setDeliveryId(invoicesDetailsResponseWrapper.getData().getId());
            produceNoticeOrder.setDeliveryNo(invoicesDetailsResponseWrapper.getData().getInvoicesNo());
            produceNoticeOrder.setDeliveryBatch(produceNoticeOrder.getDeliveryBatch() + 1);
            produceNoticeOrder.setStatus(PNOAllStatusEnum.TO_BE_PROCESS_EXAM_DELIVERY.getCode());
            produceNoticeOrder.setProcessInnerStatus(PNOProcessInnerStatusEnum.TO_BE_EXAM_DELIVERY.getCode());
            produceNoticeOrderRepository.saveAndFlush(produceNoticeOrder);
        }else {
            log.error("调用仓库服务异常:" + invoicesDetailsResponseWrapper.getMessage());
            throw new BusinessException(ResponseCode.SERVICE_WAREHOUSE_ERROR);
        }

        return Wrapper.success();
    }

    /**
     * 审核
     * */
    @Override
    @Transactional
    public Wrapper<Void> exam(CommonIdDTO dto, UserLoginCacheDTO user) {
        //根据ID获取生产通知单详情
        ProduceNoticeOrderDO produceNoticeOrder = produceNoticeOrderRepository.findById(dto.getId()).orElse(null);
        if(produceNoticeOrder == null) {
            return Wrapper.fail(ResponseCode.ENHANCE_BY_ID_NOT_FOUND_PNO);
        }
        if(!produceNoticeOrder.getStatus().equals(PNOAllStatusEnum.TO_BE_PROCESS_EXAM_DELIVERY.getCode())) {
            return Wrapper.fail(ResponseCode.ENHANCE_ONLY_EXAM_INNER_STATUS_AS_TO_BE_PROCESS_EXAM_DELIVERY);
        }

        //定义一个标识：配送方式是否为物流方式
        boolean isLogistics = produceNoticeOrder.getDeliveryType().equals(DeliveryTypeEnum.LOGISTICS.getCode());

        //根据生产通知单ID获取发货状态为待确认收货的生产通知单收发货明细
        PNOReceiveDeliverDetailDO pnoReceiveDeliverDetailDO = pnoReceiveDeliverDetailRepository.findByProduceNoticeOrderIdAndDeliverStatus(dto.getId(), DeliverStatusEnum.TO_BE_CONFIRM_DELIVERY.getCode());
        if(pnoReceiveDeliverDetailDO == null) {
            return Wrapper.fail(ResponseCode.ENHANCE_BY_ID_NOT_FOUND_PNO_RECEIVE_DELIVER_DETAIL);
        }

        //调用仓库服务 -> 审核单据内部接口
        InvoicesDetailsListRequest invoicesDetailsListRequest = new InvoicesDetailsListRequest();
        invoicesDetailsListRequest.setInvoicesId(pnoReceiveDeliverDetailDO.getDeliveryId());
        invoicesDetailsListRequest.setMemberId(user.getMemberId());
        invoicesDetailsListRequest.setMemberRoleId(user.getMemberRoleId());
        Wrapper<InvoicesDetailsResponse> wrapper = invoicesControllerFeign.invoicesReview(invoicesDetailsListRequest);
        if(ResponseCode.SUCCESS.getCode() == wrapper.getCode()) {
            //调用工作流服务 -> 执行外部流程
            TaskExecuteVO taskExecuteVO = new TaskExecuteVO();
            taskExecuteVO.setMemberId(produceNoticeOrder.getSupplierMemberId());
            taskExecuteVO.setRoleId(produceNoticeOrder.getSupplierRoleId());
            taskExecuteVO.setDataId(produceNoticeOrder.getId());
            taskExecuteVO.setTaskId(produceNoticeOrder.getOuterTaskId());
            taskExecuteVO.setAgree(isLogistics ? ProcessTaskStatusEnum.AGREE.getCode() : ProcessTaskStatusEnum.DISAGREE.getCode());
            taskExecuteVO.setProcessKey(produceNoticeOrder.getOuterProcessKey());
            Wrapper<SimpleTaskCompleteVO> outerWrapper = processFeign.completeSimpleTask(taskExecuteVO);
            if(ResponseCode.SUCCESS.getCode() == outerWrapper.getCode()) {
                //记录外部工作流记录
                OuterWorkflowRecordDO outerWorkflowRecordDO = new OuterWorkflowRecordDO();
                outerWorkflowRecordDO.setProduceNoticeOrderId(produceNoticeOrder.getId());
                outerWorkflowRecordDO.setUserId(user.getUserId());
                outerWorkflowRecordDO.setMemberId(user.getMemberId());
                outerWorkflowRecordDO.setRoleId(user.getMemberRoleId());
                outerWorkflowRecordDO.setStep(produceNoticeOrder.getOuterStatus());
                outerWorkflowRecordDO.setRoleName(user.getMemberRoleName());
                outerWorkflowRecordDO.setStatus("待新增物流单");
                outerWorkflowRecordDO.setOperate("新增加工发货单");
                outerWorkflowRecordDO.setOperateTime(System.currentTimeMillis());
                outerWorkflowRecordDO.setOpinion("发货单号：" + produceNoticeOrder.getDeliveryNo());
                outerWorkflowRecordRepository.saveAndFlush(outerWorkflowRecordDO);

                //记录加工企业内部工作流记录
                InnerWorkflowRecordDO innerWorkflowRecord = new InnerWorkflowRecordDO();
                innerWorkflowRecord.setProduceNoticeOrderId(produceNoticeOrder.getId());
                innerWorkflowRecord.setUserId(user.getUserId());
                innerWorkflowRecord.setMemberId(user.getMemberId());
                innerWorkflowRecord.setRoleId(user.getMemberRoleId());
                innerWorkflowRecord.setType(RoleTypeEnum.PROCESS.getCode());
                innerWorkflowRecord.setStep(produceNoticeOrder.getStatus());
                innerWorkflowRecord.setOperator(user.getUserName());
                innerWorkflowRecord.setDepartment(user.getUserOrgName());
                innerWorkflowRecord.setJobTitle(user.getUserJobTitle());
                innerWorkflowRecord.setStatus("已审核加工发货单");
                innerWorkflowRecord.setOperate("新增加工发货单");
                innerWorkflowRecord.setOperateTime(System.currentTimeMillis());
                innerWorkflowRecord.setOpinion("发货单号：" + produceNoticeOrder.getDeliveryNo());
                innerWorkflowRecordRepository.saveAndFlush(innerWorkflowRecord);

                //更新外部状态、外部任务ID、状态、加工企业内部状态
                produceNoticeOrder.setOuterStatus(outerWrapper.getData().getStatus());
                produceNoticeOrder.setOuterTaskId(outerWrapper.getData().getTaskId());
                produceNoticeOrder.setStatus(isLogistics ? PNOAllStatusEnum.TO_BE_PROCESS_ADD_LOGISTICS.getCode() : PNOAllStatusEnum.TO_BE_PROCESS_CONFIRM_DELIVERY.getCode());
                produceNoticeOrder.setProcessInnerStatus(isLogistics ? PNOProcessInnerStatusEnum.TO_BE_ADD_LOGISTICS.getCode() : PNOProcessInnerStatusEnum.TO_BE_CONFIRM_DELIVERY.getCode());
                produceNoticeOrderRepository.saveAndFlush(produceNoticeOrder);
            }else {
                log.error("调用工作流服务失败, 导致前面去调用了仓库服务的审核单据接口更新了发货单的审核状态, 需要将发货单回滚回待审核状态且相关业务");
                log.error("工作流服务异常:" + outerWrapper.getMessage());
                throw new BusinessException(ResponseCode.ENHANCE_WORKFLOW_ERROR);
            }
        }else {
            log.error("调用仓库服务异常:" + wrapper.getMessage());
            throw new BusinessException(ResponseCode.SERVICE_WAREHOUSE_ERROR);
        }

        //调用报表服务、消息服务 -> 新增操作通知、发送系统消息（如果调用过程出现异常, 无需回滚, 直接catch打印日志正常返回。）
        reportAsyncService.addOperateNotice(
                user.getMemberId(), user.getMemberRoleId(), OperateDataSourceEnum.ENHANCE_PROCESS.getCode(),
                isLogistics ? EnhanceProcessOperateTypeEnum.TO_BE_ADD_LOGISTICS.getCode() : EnhanceProcessOperateTypeEnum.TO_BE_DELIVERY.getCode(),  1L,
                EnhanceProcessOperateTypeEnum.TO_BE_ADD_DELIVERY.getCode(), 1L
        );
        messageAsyncService.sendSystemMessage(produceNoticeOrder.getProcessMemberId(), produceNoticeOrder.getProcessRoleId(),
                isLogistics ? MessageTemplateCode.production_order_handle_logistics : MessageTemplateCode.production_order_handle_shipment,
                Stream.of(produceNoticeOrder.getNoticeNo(), produceNoticeOrder.getSummary()).collect(Collectors.toList())
        );

        return Wrapper.success();
    }

    /**
     * 内部接口：修改发货单
     * */
    @Override
    @Transactional
    public Wrapper<Void> updateDelivery(UpdateInvoiceDTO dto) {
        //根据ID获取生产通知单详情
        ProduceNoticeOrderDO produceNoticeOrder = produceNoticeOrderRepository.findById(dto.getProduceNoticeOrderId()).orElse(null);
        if(produceNoticeOrder == null) {
            return Wrapper.fail(ResponseCode.ENHANCE_BY_ID_NOT_FOUND_PNO);
        }

        //根据生产通知单ID和发货单ID获取生产通知单收发货明细
        PNOReceiveDeliverDetailDO pnoReceiveDeliverDetail = pnoReceiveDeliverDetailRepository.findByProduceNoticeOrderIdAndDeliveryId(dto.getProduceNoticeOrderId(), dto.getDeliveryId());
        if(pnoReceiveDeliverDetail == null) {
            return Wrapper.fail(ResponseCode.ENHANCE_BY_ID_NOT_FOUND_PNO_RECEIVE_DELIVER_DETAIL);
        }
        if(pnoReceiveDeliverDetail.getStorageId() != null) {
            log.info("入库单ID不为空说明供应商已新增了入库单");
            return Wrapper.fail("供应商新增了入库单后就不能修改发货单");
        }

        //如果修改的这张发货单是已被加工企业发货出去的话, 那需要将发货时对生产通知单明细的统计进行还原。（比如：A商品发货数量加了1000，那么现在就要减1000）
        if(pnoReceiveDeliverDetail.getDeliverStatus().equals(DeliverStatusEnum.ALREADY_CONFIRM_DELIVERY.getCode())) {
            for(PNOReceiveDeliverDetailProductBO bo:pnoReceiveDeliverDetail.getPnoReceiveDeliverDetailProductBOList()) {
                ProduceNoticeOrderDetailDO produceNoticeOrderDetailDO = produceNoticeOrderDetailRepository.findById(bo.getProduceNoticeOrderDetailId()).orElse(null);
                if(produceNoticeOrderDetailDO != null) {
                    produceNoticeOrderDetailDO.setDeliverNum(produceNoticeOrderDetailDO.getDeliverNum() - bo.getDeliverNum());
                    produceNoticeOrderDetailDO.setNotDeliverNum(produceNoticeOrderDetailDO.getProcessNum() + produceNoticeOrderDetailDO.getDeliverNum());
                    produceNoticeOrderDetailDO.setDifferenceNum(produceNoticeOrderDetailDO.getDeliverNum() + produceNoticeOrderDetailDO.getReceiveNum());
                    produceNoticeOrderDetailRepository.saveAndFlush(produceNoticeOrderDetailDO);
                }
            }
        }

        //根据生产通知单明细ID进行分组, 获取到每个生产通知单明细的发货数量总和。【key:生产通知单明细ID, value:发货数量总和】
        Map<Long, Double> map = dto.getInvoiceDetailsDTOList().stream().collect(Collectors.groupingBy(UpdateInvoiceDTO.InvoiceDetailsDTO::getProduceNoticeOrderDetailId, Collectors.summingDouble(UpdateInvoiceDTO.InvoiceDetailsDTO::getShipmentQuantity)));

        //获取生产通知单明细集合, 拼装生产通知单收发货明细商品（注意这里keySet：这里获取的生产通知单明细并非是生产通知单下的所有明细, 而是选择发货的货品对应的生产通知单明细）
        List<ProduceNoticeOrderDetailDO> produceNoticeOrderDetailDOList = produceNoticeOrderDetailRepository.findAllById(map.keySet());
        List<PNOReceiveDeliverDetailProductBO> pnoReceiveDeliverDetailProductBOList = produceNoticeOrderDetailDOList.stream().map(a -> {
            PNOReceiveDeliverDetailProductBO pnoReceiveDeliverDetailProductBO = new PNOReceiveDeliverDetailProductBO();
            BeanUtils.copyProperties(a, pnoReceiveDeliverDetailProductBO);
            pnoReceiveDeliverDetailProductBO.setDeliverNum(map.get(a.getId()));
            pnoReceiveDeliverDetailProductBO.setProduceNoticeOrderDetailId(a.getId());
            return pnoReceiveDeliverDetailProductBO;
        }).collect(Collectors.toList());

        //修改生产通知单收发货明细
        pnoReceiveDeliverDetail.setPnoReceiveDeliverDetailProductBOList(pnoReceiveDeliverDetailProductBOList);
        pnoReceiveDeliverDetailRepository.saveAndFlush(pnoReceiveDeliverDetail);

        //同上, 如果修改的这张发货单是已被加工企业发货出去的话, 需要对生产通知单明细进行统计
        if(pnoReceiveDeliverDetail.getDeliverStatus().equals(DeliverStatusEnum.ALREADY_CONFIRM_DELIVERY.getCode())) {
            for(PNOReceiveDeliverDetailProductBO bo:pnoReceiveDeliverDetail.getPnoReceiveDeliverDetailProductBOList()) {
                ProduceNoticeOrderDetailDO produceNoticeOrderDetailDO = produceNoticeOrderDetailRepository.findById(bo.getProduceNoticeOrderDetailId()).orElse(null);
                if(produceNoticeOrderDetailDO != null) {
                    produceNoticeOrderDetailDO.setDeliverNum(produceNoticeOrderDetailDO.getDeliverNum() + bo.getDeliverNum());
                    produceNoticeOrderDetailDO.setNotDeliverNum(produceNoticeOrderDetailDO.getProcessNum() - produceNoticeOrderDetailDO.getDeliverNum());
                    produceNoticeOrderDetailDO.setDifferenceNum(produceNoticeOrderDetailDO.getDeliverNum() - produceNoticeOrderDetailDO.getReceiveNum());
                    produceNoticeOrderDetailRepository.saveAndFlush(produceNoticeOrderDetailDO);
                }
            }
        }

        return Wrapper.success();
    }

    /**
     * 内部接口：批量审核发货单
     * */
    @Override
    @Transactional
    public Wrapper<Void> batchExamDelivery(BatchExamDTO dto) {
        //根据ID集合获取生产通知单集合
        List<ProduceNoticeOrderDO> produceNoticeOrderList = produceNoticeOrderRepository.findAllById(dto.getIdList());

        //校验
        if(CollectionUtil.isEmpty(produceNoticeOrderList)) {
            return Wrapper.fail(ResponseCode.ENHANCE_BY_ID_NOT_FOUND_PNO);
        }
        if(produceNoticeOrderList.stream().anyMatch(a -> !a.getStatus().equals(PNOAllStatusEnum.TO_BE_PROCESS_EXAM_DELIVERY.getCode()))) {
            return Wrapper.fail(ResponseCode.ENHANCE_ONLY_EXAM_INNER_STATUS_AS_TO_BE_PROCESS_EXAM_DELIVERY);
        }
        if(dto.getUser() == null) {
            return Wrapper.fail("当前登录会员信息不能为空");
        }

        //循环调用工作流服务, 调用成功时更新生产通知单, 调用失败直接返回, 不需要抛出异常, 因为前面调用成功的不需要回滚。
        for(ProduceNoticeOrderDO p:produceNoticeOrderList) {
            //定义一个标识：配送方式是否为物流方式
            boolean isLogistics = p.getDeliveryType().equals(DeliveryTypeEnum.LOGISTICS.getCode());

            //根据生产通知单ID获取发货状态为待确认收货的生产通知单收发货明细
            PNOReceiveDeliverDetailDO pnoReceiveDeliverDetailDO = pnoReceiveDeliverDetailRepository.findByProduceNoticeOrderIdAndDeliverStatus(p.getId(), DeliverStatusEnum.TO_BE_CONFIRM_DELIVERY.getCode());
            if(pnoReceiveDeliverDetailDO == null) {
                return Wrapper.fail(ResponseCode.ENHANCE_BY_ID_NOT_FOUND_PNO_RECEIVE_DELIVER_DETAIL);
            }

            //调用工作流服务 -> 执行外部流程
            TaskExecuteVO taskExecuteVO = new TaskExecuteVO();
            taskExecuteVO.setMemberId(p.getSupplierMemberId());
            taskExecuteVO.setRoleId(p.getSupplierRoleId());
            taskExecuteVO.setDataId(p.getId());
            taskExecuteVO.setTaskId(p.getOuterTaskId());
            taskExecuteVO.setAgree(isLogistics ? ProcessTaskStatusEnum.AGREE.getCode() : ProcessTaskStatusEnum.DISAGREE.getCode());
            taskExecuteVO.setProcessKey(p.getOuterProcessKey());
            Wrapper<SimpleTaskCompleteVO> outerWrapper = processFeign.completeSimpleTask(taskExecuteVO);
            if(ResponseCode.SUCCESS.getCode() == outerWrapper.getCode()) {
                //记录外部工作流记录
                OuterWorkflowRecordDO outerWorkflowRecordDO = new OuterWorkflowRecordDO();
                outerWorkflowRecordDO.setProduceNoticeOrderId(p.getId());
                outerWorkflowRecordDO.setUserId(dto.getUser().getUserId());
                outerWorkflowRecordDO.setMemberId(dto.getUser().getMemberId());
                outerWorkflowRecordDO.setRoleId(dto.getUser().getMemberRoleId());
                outerWorkflowRecordDO.setStep(p.getOuterStatus());
                outerWorkflowRecordDO.setRoleName(dto.getUser().getMemberRoleName());
                outerWorkflowRecordDO.setStatus("待新增物流单");
                outerWorkflowRecordDO.setOperate("新增加工发货单");
                outerWorkflowRecordDO.setOperateTime(System.currentTimeMillis());
                outerWorkflowRecordDO.setOpinion("发货单号：" + p.getDeliveryNo());
                outerWorkflowRecordRepository.saveAndFlush(outerWorkflowRecordDO);

                //记录加工企业内部工作流记录
                InnerWorkflowRecordDO innerWorkflowRecord = new InnerWorkflowRecordDO();
                innerWorkflowRecord.setProduceNoticeOrderId(p.getId());
                innerWorkflowRecord.setUserId(dto.getUser().getUserId());
                innerWorkflowRecord.setMemberId(dto.getUser().getMemberId());
                innerWorkflowRecord.setRoleId(dto.getUser().getMemberRoleId());
                innerWorkflowRecord.setType(RoleTypeEnum.PROCESS.getCode());
                innerWorkflowRecord.setStep(p.getStatus());
                innerWorkflowRecord.setOperator(dto.getUser().getUserName());
                innerWorkflowRecord.setDepartment(dto.getUser().getUserOrgName());
                innerWorkflowRecord.setJobTitle(dto.getUser().getUserJobTitle());
                innerWorkflowRecord.setStatus("已审核加工发货单");
                innerWorkflowRecord.setOperate("新增加工发货单");
                innerWorkflowRecord.setOperateTime(System.currentTimeMillis());
                innerWorkflowRecord.setOpinion("发货单号：" + p.getDeliveryNo());
                innerWorkflowRecordRepository.saveAndFlush(innerWorkflowRecord);

                //更新外部状态、外部任务ID、状态、加工企业内部状态
                p.setOuterStatus(outerWrapper.getData().getStatus());
                p.setOuterTaskId(outerWrapper.getData().getTaskId());
                p.setStatus(isLogistics ? PNOAllStatusEnum.TO_BE_PROCESS_ADD_LOGISTICS.getCode() : PNOAllStatusEnum.TO_BE_PROCESS_CONFIRM_DELIVERY.getCode());
                p.setProcessInnerStatus(isLogistics ? PNOProcessInnerStatusEnum.TO_BE_ADD_LOGISTICS.getCode() : PNOProcessInnerStatusEnum.TO_BE_CONFIRM_DELIVERY.getCode());
                produceNoticeOrderRepository.saveAndFlush(p);
            }else {
                log.error("工作流服务异常:" + outerWrapper.getMessage());
                return Wrapper.fail(ResponseCode.ENHANCE_WORKFLOW_ERROR);
            }

            //调用报表服务、消息服务 -> 新增操作通知、发送系统消息（如果调用过程出现异常, 无需回滚, 直接catch打印日志正常返回。）
            reportAsyncService.addOperateNotice(
                    dto.getUser().getMemberId(), dto.getUser().getMemberRoleId(), OperateDataSourceEnum.ENHANCE_PROCESS.getCode(),
                    isLogistics ? EnhanceProcessOperateTypeEnum.TO_BE_ADD_LOGISTICS.getCode() : EnhanceProcessOperateTypeEnum.TO_BE_DELIVERY.getCode(),  1L,
                    EnhanceProcessOperateTypeEnum.TO_BE_ADD_DELIVERY.getCode(), 1L
            );
            messageAsyncService.sendSystemMessage(p.getProcessMemberId(), p.getProcessRoleId(),
                    isLogistics ? MessageTemplateCode.production_order_handle_logistics : MessageTemplateCode.production_order_handle_shipment,
                    Stream.of(p.getNoticeNo(), p.getSummary()).collect(Collectors.toList())
            );
        }

        return Wrapper.success();
    }
}
